/*
 * main.cpp
 *
 *  Created on: May 23, 2011
 *      Author: Manslaughter
 */
#include <vector>
#include <stdio.h>
#include <omp.h>
#include <assert.h>
#include <string>
#include <iostream>
#include <time.h>
#include <Windows.h>
#include "BigInt.h"
using namespace std;
using namespace APIAL;

void PrintMethodInfo(string method, vector<int>& threads, vector<int>& op_size, vector<unsigned long>& run_time);
void randomNumberGenerator(BigInt* num, int desiredSize, int chunkSize);

int PerformanceTest(int argc, const char *argv[]) {

	int typeOfArithmetic = 1;
	int chunkSize = 31;
	int numSize = 10;
	int reps = 1000;
	int normalize = 0;
	int maxThreadSize = 8;
	vector<int> threads = std::vector<int>();
	vector<int> op_size = std::vector<int>();
	vector<unsigned long> run_time = std::vector<unsigned long>();

	for(int typeOfArithmetic = 3; typeOfArithmetic <= 4; typeOfArithmetic++){
	
	for(int numSize = 1; numSize <= 10; numSize *= 10)
	{
		threads.clear();
		op_size.clear();
		run_time.clear();

		int shift_amt = numSize/2;
	
	
		BigInt* op1 = new BigInt(numSize, chunkSize);
		BigInt* op2 = new BigInt(numSize/2, chunkSize);
		BigInt* scratch = new BigInt(numSize, chunkSize);
		BigInt* result = new BigInt(numSize*2, chunkSize);

		randomNumberGenerator(op1, numSize, chunkSize);
		randomNumberGenerator(op2, numSize/2, chunkSize);

		switch (typeOfArithmetic) {
	case 1:
		for (int thread_size = 1; thread_size <= maxThreadSize; thread_size = thread_size << 1) {
			omp_set_num_threads(thread_size);
			unsigned long start1 = clock();
			for (int rep_count = 0; rep_count < reps; rep_count++) {
				Add(op1, op2, result, scratch);
			}
			unsigned long stop1 = clock();
			unsigned long diff = stop1 - start1;
			threads.push_back(thread_size);
			op_size.push_back(numSize);
			run_time.push_back(diff);
		}
		PrintMethodInfo("Addition", threads, op_size, run_time);
		break;
	case 2:
		result->resize(numSize*2 + 1, false);
		for (int thread_size = 1; thread_size <= maxThreadSize; thread_size = thread_size << 1) {
			omp_set_num_threads(thread_size);
			unsigned long start1 = clock();
			for (int rep_count = 0; rep_count < reps; rep_count++) {
				Subtract(op1, op2, result, scratch);
			}
			unsigned long stop1 = clock();
			unsigned long diff = stop1 - start1;
			threads.push_back(thread_size);
			op_size.push_back(numSize);
			run_time.push_back(diff);
		}
		PrintMethodInfo("Subtract", threads, op_size, run_time);
		break;
	case 3:
		result->resize(numSize*2 + 1, false);
		for (int thread_size = 1; thread_size <= maxThreadSize; thread_size = thread_size << 1) {
			omp_set_num_threads(thread_size);
			unsigned long start1 = clock();
			for (int rep_count = 0; rep_count < reps; rep_count++) {
				Multiply(op1, op2, result);
			}
			unsigned long stop1 = clock();
			unsigned long diff = stop1 - start1;
			threads.push_back(thread_size);
			op_size.push_back(numSize);
			run_time.push_back(diff);
		}
		PrintMethodInfo("Multiply", threads, op_size, run_time);
		break;
	case 4:
		result->resize(numSize*2 + 1, false);
		for (int thread_size = 1; thread_size <= maxThreadSize; thread_size = thread_size << 1) {
			omp_set_num_threads(thread_size);
			unsigned long start1 = clock();
			for (int rep_count = 0; rep_count < reps; rep_count++) {
				Divide(op1, op2, result);
			}
			unsigned long stop1 = clock();
			unsigned long diff = stop1 - start1;
			threads.push_back(thread_size);
			op_size.push_back(numSize);
			run_time.push_back(diff);
		}
		PrintMethodInfo("Divide", threads, op_size, run_time);
		break;
	case 5:
		for (int thread_size = 1; thread_size <= maxThreadSize; thread_size = thread_size << 1) {
			omp_set_num_threads(thread_size);
			unsigned long start1 = clock();
			for (int rep_count = 0; rep_count < reps; rep_count++) {
				Xor(op1, op2, result);
			}
			unsigned long stop1 = clock();
			unsigned long diff = stop1 - start1;
			threads.push_back(thread_size);
			op_size.push_back(numSize);
			run_time.push_back(diff);
		}
		PrintMethodInfo("Compare", threads, op_size, run_time);
		break;
	case 6:
		for (int thread_size = 1; thread_size <= maxThreadSize; thread_size = thread_size << 1) {
			omp_set_num_threads(thread_size);
			unsigned long start1 = clock();
			for (int rep_count = 0; rep_count < reps; rep_count++) {
				Xor(op1, op2, result);
			}
			unsigned long stop1 = clock();
			unsigned long diff = stop1 - start1;
			threads.push_back(thread_size);
			op_size.push_back(numSize);
			run_time.push_back(diff);
		}
		PrintMethodInfo("XOR", threads, op_size, run_time);
		break;
	case 7:
		for (int thread_size = 1; thread_size <= maxThreadSize; thread_size = thread_size << 1) {
			omp_set_num_threads(thread_size);
			unsigned long start1 = clock();
			for (int rep_count = 0; rep_count < reps; rep_count++) {
				And(op1, op2, result);
			}
			unsigned long stop1 = clock();
			unsigned long diff = stop1 - start1;
			threads.push_back(thread_size);
			op_size.push_back(numSize);
			run_time.push_back(diff);
		}
		PrintMethodInfo("AND", threads, op_size, run_time);
		break;
	case 8:
		for (int thread_size = 1; thread_size <= maxThreadSize; thread_size = thread_size << 1) {
			omp_set_num_threads(thread_size);
			unsigned long start1 = clock();
			for (int rep_count = 0; rep_count < reps; rep_count++) {
				Not(op1, result);
			}
			unsigned long stop1 = clock();
			unsigned long diff = stop1 - start1;
			threads.push_back(thread_size);
			op_size.push_back(numSize);
			run_time.push_back(diff);
		}
		PrintMethodInfo("NOT", threads, op_size, run_time);
		break;
	case 9:
		for (int thread_size = 1; thread_size <= maxThreadSize; thread_size = thread_size << 1) {
			omp_set_num_threads(thread_size);
			unsigned long start1 = clock();
			for (int rep_count = 0; rep_count < reps; rep_count++) {
				Or(op1, op2, result);
			}
			unsigned long stop1 = clock();
			unsigned long diff = stop1 - start1;
			threads.push_back(thread_size);
			op_size.push_back(numSize);
			run_time.push_back(diff);
		}
		PrintMethodInfo("OR", threads, op_size, run_time);
		break;
	case 10:
		for (int thread_size = 1; thread_size <= maxThreadSize; thread_size = thread_size << 1) {
			omp_set_num_threads(thread_size);
			unsigned long start1 = clock();
			for (int rep_count = 0; rep_count < reps; rep_count++) {
				LeftShift(op1, shift_amt, result);
			}
			unsigned long stop1 = clock();
			unsigned long diff = stop1 - start1;
			threads.push_back(thread_size);
			op_size.push_back(numSize);
			run_time.push_back(diff);
		}
		PrintMethodInfo("LeftShift", threads, op_size, run_time);
		break;
	case 11:
		for (int thread_size = 1; thread_size <= maxThreadSize; thread_size = thread_size << 1) {
			omp_set_num_threads(thread_size);
			unsigned long start1 = clock();
			for (int rep_count = 0; rep_count < reps; rep_count++) {
				RightShift(op1, shift_amt, result);
			}
			unsigned long stop1 = clock();
			unsigned long diff = stop1 - start1;
			threads.push_back(thread_size);
			op_size.push_back(numSize);
			run_time.push_back(diff);
		}
		PrintMethodInfo("RightShift", threads, op_size, run_time);
		break;
	default:
		return -1;
	}
	
	free(op1);
	free(op2);
	free(result);
	free(scratch);
	}}

	return 1;
}

void PrintMethodInfo(string method, vector<int>& threads, vector<int>& op_size, vector<unsigned long>& run_time)
{
	cout << method << endl;

	for(int i = 0; i < (int)threads.size(); i++)
	{
		cout << threads.at(i) << ", " << op_size.at(i) << ", " << run_time.at(i) << endl;
	}

	cout << endl << endl;
}

void randomNumberGenerator(BigInt* num, int desiredSize, int chunkSize) {
	// generate random seed
	srand((unsigned int)time(NULL));
	num->size = desiredSize;
	int maxValue = (1 << (chunkSize - 1)) - 1;

	// add random numbers to chunks
	for (int i = 0; i < desiredSize; i++)
		num->chunks[i] = rand() % maxValue;
}